package com.starsoft.smdc.crawler;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import com.starsoft.frame.util.StringUtil;
import com.starsoft.smdc.bean.SmdcDiv;
import com.starsoft.smdc.bean.SmdcEr;
import com.starsoft.smdc.bean.SmdcErId;
import com.starsoft.smdc.bean.SmdcMarketDaily;
import com.starsoft.smdc.bean.SmdcSecurity;
import com.starsoft.smdc.crawler.common.HandicapCrawlerImpl;
import com.starsoft.smdc.crawler.danjuan.MarketDailyCsi;
import com.starsoft.smdc.crawler.danjuan.MarketDailyDanjuan;
import com.starsoft.smdc.crawler.sina.DivSina;
import com.starsoft.smdc.crawler.sina.MarketDailySina;
import com.starsoft.smdc.crawler.ths.ErThs;
import com.starsoft.smdc.crawler.xueqiu.DivXueqiu;
import com.starsoft.smdc.crawler.xueqiu.MarketDailyV5Xueqiu;
import com.starsoft.smdc.service.DivService;
import com.starsoft.smdc.service.ErService;
import com.starsoft.smdc.service.HolidayService;
import com.starsoft.smdc.service.MarketDailyService;
import com.starsoft.smdc.service.SecurityService;
import com.starsoft.smdc.util.SecurityType;

@Component
public class SmdcUpdateService {
	
	private static Logger logger = LoggerFactory.getLogger(SmdcUpdateService.class); 
	
	@Autowired
	SecurityService securityService;
	
	@Autowired
	MarketDailyService marketDailyService;
	
	@Autowired
	MarketDailyV5Xueqiu marketDailyXueqiu;
	
	@Autowired
	MarketDailyDanjuan marketDailyDanjuan;
	
	@Autowired
	MarketDailyCsi marketDailyCsi;
	
	@Autowired
	MarketDailySina marketDailySina;
	
	@Autowired
	ErService erService;

	@Autowired
	ErThs erThs; 
	
	@Autowired
	DivService divService;
	
	@Autowired
	DivXueqiu divXueqiu;
	
	@Autowired
	DivSina divSina;
	
	@Autowired
	HolidayService holidayService;
	
	@Autowired
	HandicapCrawlerImpl handicapNetease;
	
	public List<SmdcMarketDaily> updateMarket(SmdcSecurity security, Date beginDate){
		List<SmdcSecurity> securityList = Arrays.asList(security);
		List<SmdcMarketDaily> result = xueqiuUpdate(securityList, beginDate);
		result.addAll(sinaUpdate(securityList, beginDate));
		result.addAll(handicapUpdate(securityList, beginDate));
		result.addAll(danjuanUpdate(securityList, beginDate));
		updateAjdFactor(result, securityList);
		return removeRepeatMarket(result);
	}
	
	public List<SmdcMarketDaily> updateMarketAll(Date defaultbeginDate){
		List<SmdcSecurity> securityList = securityService.getAll();
		List<SmdcMarketDaily> result = xueqiuUpdate(securityList, defaultbeginDate);
		result.addAll(sinaUpdate(securityList, defaultbeginDate));
		result.addAll(handicapUpdate(securityList, defaultbeginDate));
		result.addAll(danjuanUpdate(securityList, defaultbeginDate));
		updateAjdFactor(result, securityList);
		return removeRepeatMarket(result);
	}
	
	private List<SmdcMarketDaily> removeRepeatMarket(List<SmdcMarketDaily> markets){
		Map<String, SmdcMarketDaily> resultMap = new HashMap<>();
		markets.forEach(market -> resultMap.put(market.getId().toString(), market));
		return new ArrayList<>(resultMap.values());
	}
	
	private Date getMarketBeginDate(SmdcSecurity security, Date beginDate){
		Date resultDate = marketDailyService.getBeginDate(security);
		if (beginDate != null && resultDate.after(beginDate)) {
			resultDate = beginDate;
		}
		return resultDate;
	}
	
	private Date getNavBeginDate(SmdcSecurity security, Date beginDate){
		Date resultDate = marketDailyService.getNavBeginDate(security);
		if (beginDate != null && resultDate.after(beginDate)) {
			resultDate = beginDate;
		}
		return resultDate;
	}
	
	private List<SmdcMarketDaily> xueqiuUpdate(List<SmdcSecurity> securityList, Date defaultbeginDate){
		List<SmdcSecurity> securitys = new ArrayList<>();
		List<Date> beginDates = new ArrayList<>();
		for (SmdcSecurity security : securityList) {
			Date beginDate = getMarketBeginDate(security, defaultbeginDate);
			if(!securityService.isDlist(security, beginDate)){
				securitys.add(security);
				beginDates.add(beginDate);
			}
		}
		List<SmdcMarketDaily> result = marketDailyXueqiu.update(securitys, beginDates);
		return result;
	}
	
	private List<SmdcMarketDaily> danjuanUpdate(List<SmdcSecurity> securityList, Date defaultbeginDate){
		List<SmdcSecurity> securitys = new ArrayList<>();
		List<Date> beginDates = new ArrayList<>();
		for (SmdcSecurity security : securityList) {
			if (StringUtil.isEmpty(security.getFullEarningIndex()) && !"csi".equals(security.getExchange()))
				continue;
			Date resultDate = marketDailyService.getFullEarningBeginDate(security);
			if (defaultbeginDate != null && resultDate.after(defaultbeginDate)) {
				resultDate = defaultbeginDate;
			}
			if(!securityService.isDlist(security, resultDate)){
				securitys.add(security);
				beginDates.add(resultDate);
			}
		}
		List<SmdcMarketDaily> result = marketDailyCsi.update(securitys, beginDates);
		
		securitys.clear();
		beginDates.clear();
		for (SmdcSecurity security : securityList) {
			if (security.getCrawleValuation() == null || !security.getCrawleValuation()) continue;
			Date resultDate = marketDailyService.getValuationBeginDate(security);
			if (defaultbeginDate != null && resultDate.after(defaultbeginDate)) {
				resultDate = defaultbeginDate;
			}
			if(!securityService.isDlist(security, resultDate)){
				securitys.add(security);
				beginDates.add(resultDate);
			}
		}
		
		result.addAll(marketDailyDanjuan.update(securitys, beginDates));
		
		return result;
	}
	
	private List<SmdcMarketDaily> sinaUpdate(List<SmdcSecurity> securityList, Date defaultbeginDate){
		List<SmdcSecurity> securitys = new ArrayList<>();
		List<Date> beginDates = new ArrayList<>();
		for (SmdcSecurity security : securityList) {
			Date beginDate = getNavBeginDate(security, defaultbeginDate);
			if (!securityService.isDlist(security, beginDate)) {
				securitys.add(security);
				beginDates.add(beginDate);
			}
		}
		List<SmdcMarketDaily> result = marketDailySina.update(securitys, beginDates);
		return result;
	}
	
	private List<SmdcMarketDaily> handicapUpdate(List<SmdcSecurity> securityList, Date defaultbeginDate){
		List<SmdcSecurity> securitys = new ArrayList<>();
		List<Date> beginDates = new ArrayList<>();
		for (SmdcSecurity security : securityList) {
			if("of".equalsIgnoreCase(security.getExchange())) continue;
			Date beginDate = marketDailyService.getHandicapBeginDate(security);
			if (defaultbeginDate != null && beginDate.after(defaultbeginDate)) {
				beginDate = defaultbeginDate;
			}
			if(!securityService.isDlist(security, beginDate)){
				securitys.add(security);
				beginDates.add(beginDate);
			}
		}
		List<SmdcMarketDaily> result = handicapNetease.update(securitys, beginDates);
		return result;
	}
	
	private void updateAjdFactor(List<SmdcMarketDaily> result, List<SmdcSecurity> securitys) {
		Map<String, Date> accBeginDateMap = new HashMap<>();
		for (SmdcMarketDaily market : result) {
			String secId = market.getId().getSecId();
			Date tradeDate = market.getId().getTradeDate();
			Date date = accBeginDateMap.get(secId);
			if (date == null || tradeDate.before(date)) {
				accBeginDateMap.put(secId, tradeDate);
			}
		}
		for (SmdcSecurity security : securitys) {
			String secId = security.getSecId();
			if (accBeginDateMap.containsKey(secId)) {
				try {
					marketDailyService.updateAdjFactor(security, accBeginDateMap.get(secId));
				} catch (Exception e) {
					logger.error("Failed to Update Adj Factor for {}", security.getSecId());
				}
			}
		}
	}
	
	public List<SmdcDiv> updateDiv(SmdcSecurity security, Date beginDate) {
		if (!Boolean.TRUE.equals(security.getCrawleDiv())) {
			return new ArrayList<SmdcDiv>();
		}
		if (security.getDlistDate()!=null) {
			return new ArrayList<SmdcDiv>();
		}
		if (beginDate == null) {
			beginDate = divService.getBeginDate(security);
		}
		List<SmdcDiv> result = divXueqiu.update(security, beginDate);
		result.addAll(divSina.update(security, beginDate));
		erService.update(security, beginDate);
		marketDailyService.updateAdjFactor(security, beginDate);
		return result;
	}
	
	public List<SmdcDiv> updateDivAll() {
		List<SmdcSecurity> securityList = securityService.getAll();
		List<SmdcDiv> result = new ArrayList<>();
		for (SmdcSecurity security : securityList) {
			try {
				result.addAll(updateDiv(security, null));
			} catch (Exception e) {
				logger.error("update adjust factor failed:" + security.getSecId(), e);
			}
		}
		return result;
	}
	
	public List<SmdcEr> updateEr(SmdcSecurity security, Date beginDate) {
		if (!SecurityType.isStock(security.getSecType())) {
			return new ArrayList<SmdcEr>();
		}
		if (!Boolean.TRUE.equals(security.getCrawleEr())) {
			return new ArrayList<SmdcEr>();
		}
		if (beginDate == null) {
			beginDate = erService.getBeginDate(security);
		}
		if (securityService.isDlist(security, beginDate)) {
			return new ArrayList<SmdcEr>();
		}
		List<SmdcEr> result = erThs.update(security, beginDate);
		erService.update(security, beginDate);
		return removeRepeatEr(result);
	}
	

	public List<SmdcEr> updateErAll() {
		List<SmdcSecurity> securityList = securityService.getAll();
		List<SmdcEr> result = new ArrayList<SmdcEr>();
		for (SmdcSecurity security : securityList) {
			try {
				result.addAll(updateEr(security, null));
			} catch (Exception e) {
				logger.error("update failed " + security.getSecId(), e);
			}
		}
		return removeRepeatEr(result);
	}
	
	private List<SmdcEr> removeRepeatEr(List<SmdcEr> ers) {
		Map<SmdcErId, SmdcEr> resultMap = new HashMap<>();
		ers.forEach(er -> resultMap.put(er.getId(), er));
		return new ArrayList<>(resultMap.values());
	}
	
	public void updateAll(SmdcSecurity security){
		updateMarket(security, null);
		updateDiv(security, null);
		updateEr(security, null);
	}
	
}
